闲话 22.11.16

闲话

今天给我整了个好活 普通三维偏序的 O(nlogn) 解法
那就要问了,形如 aiaj,bibj,djcicj 的加强版三维偏序有没有什么 o(nlog2n) 的做法
如果有人搞出来了可以在提交(

有什么好题当社论题材吗?
最近想写ARC148F来着

啊对了今天放冠世一战了!好听!

杂题

AGC056B
给定整数 n 以及 m 对整数。第 i 对整数为 (li,ri)

请输出可以通过如下方式生成的整数序列 x=(x1,x2,,xm) 的个数。答案对 998244353 取模。

生成方式:

  • 取排列 p=(p1,p2,,pn),满足其为一个 1n 的排列。
  • 对于任意 1imi,令 xiplipli+1,,pri 中最大值对应的下标。即 pxi=max{plipli+1,,pri}

2n300, 1mn(n1)2

为啥这场 B 比 C 难啊
C 是个 2481 比这一段的 C 都简单 B 是个 nmd 3339 比这一段的 C 都难
A 是个 sb 构造,暂且不表

同一个 x 可能对应多个 p,因此这样计数比较困难。
考虑反过来计数。

对于给定的 x,我们将按照以下的方式构造 p

  • p=(1,1,,1)
  • 我们从 n 开始依次递减地考虑每个值 v。对于每个值,我们找到 v 能放的最左侧的位置,放进去。

计数可以通过这种方式生成的 p

设当前最值为 v。我们首先确定下标 m,使得 pm=v。对于所有包含 m 的区间 i,有 xi=m。删除这些包含 m 的区间后,我们可以分别考虑位于 m 左右两侧的区间。由于 m 为最左侧的可以放 v 的位置,右侧的数均小于左侧的数,这部分是和原问题等价但规模更小的子问题。

现在考虑左侧。我们令 km 左侧最大元素对应的下标。有:必定存在一个左侧区间同时包含 km
考虑反证。如果左侧没有区间同时包含 km,那我们可以令 pk=v,这必定是更优且满足要求的。这与假设矛盾,因此必定存在同时包含 km 的左侧区间。
因此,左侧区间所填的数的最大值必定大于等于 v

考虑区间 dp。我们设 f(l,r,v)[l,r] 区间满足最大值大于等于 v 的方案数。可以通过枚举中点以及预处理区间最大值的方式转移。转移时加入后缀和即可快速得到最终答案。

时间复杂度 O(n3)

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; 
template <typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
template <typename T1, typename T2> T1 min(T1 a, T2 b) { T1 _b = static_cast<T1>(b); return a < b ? a : b; }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 5e2 + 10;

const int mod = 998244353;
const int inv2 = (mod + 1) >> 1;
template <typename T1, typename T2> T1 add(T1 a, T2 b) { return (a += b) >= mod ? a - mod : a; }
template <typename T1, typename ...Args> T1 add(T1 a, Args ... b) { return add(a, add(b...)); }
struct FastMod { int m; ll b; void init(int _m) { m = _m; if (m == 0) m = 1; b = ((lll)1<<64) / m; } FastMod(int _m) { init(_m); } int operator() (ll a) {ll q = ((lll)a * b) >> 64; a -= q * m; if (a >= m) a -= m; return a; } } Mod(mod);
int mul(int a, int b) { return Mod(1ll * a * b); } template <typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }
template <typename T1, typename T2> T1 qp(T1 a, T2 b) { T1 ret = 1; for (; b > 0; a = mul(a, a), b >>= 1) if (b & 1) ret = mul(ret, a); return ret; }

int n, m, f[N][N][N], g[N][N][N];
pii v[N * (N - 1) >> 1];

int main() {
	get(n, m); rep(i,1,m) get(v[i].first, v[i].second);
	rep(i,0,n+1) rep(j,0,n+1) rep(k,0,n+1) g[i][j][k] = n + 1;
	rep(i,1,m) rep(k,v[i].first,v[i].second) g[v[i].first][v[i].second][k] = min(g[v[i].first][v[i].second][k], v[i].first);
	pre(l,n,1) rep(r,l+1,n) rep(k,l,r) g[l][r][k] = min( { g[l][r][k], g[l+1][r][k], g[l][r-1][k] } );
	rep(l,1,n+1) rep(k,1,n+1) f[l][l-1][k] = 1;
	pre(l,n,1) rep(r,l,n) pre(k,r,l) f[l][r][k] = add(f[l][r][k + 1], mul(f[l][k - 1][g[l][r][k]], f[k + 1][r][k + 1])); 
	cout << f[1][n][1] << '\n';
}



AGC056C

你需要构造一个长度为 n 、由 01 组成的字符串,同时需要满足 m 个条件。第 i 个条件由两个整数 li, ri 给出,表示字符串位于 [li,ri] 区间的字符必须是相同数量的 01

请输出满足所有条件且字典序最小的字符串。可以证明在题设条件下总存在至少一个字符串满足所有条件。

2n106, 1m2×105, 1li<rin,(rili+1)mod2, (li,ri)(lj,rj)(i!=j)

直接做不好做,考虑将条件转化。令 di1i0 的个数减去 1 的个数。
容易发现,想要最小化原串的字典序,其实就是让 0 尽可能多,也就是让 di 尽可能大。这就转化为如何 di 的字典序最大。

我们发现,给定的条件 (li,ri) 可以表述为 dri=dli1,因为 [li,ri] 段中 0,1 个数是相等的,体现在 di 上就是 [li,ri] 段有 0 的贡献。
同时根据定义可得  1<in, |didi1|=1。这与  1<in, |didi1|1 等价。

列出我们已知的条件:

  1. 1im, dli1=dri
  2.  1<in, |didi1|1

我们需要使字典序最大,这启发我们采用差分约束系统求解本题。
1. 条件建 (li1,ri),边权为 0 的双向边;对 2. 条件建 (i1,i),边权为 1 的双向边。
由于边权的特殊性,我们可以通过 01bfs 求解所有 di。这给出了构造方案。

总时间复杂度 O(n+m)

code
#include <bits/stdc++.h>
using namespace std; 
template <typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e6 + 10;
int n, m, l, r; 
vector <pair<int,int> > g[N];

int dis[N];
deque<int> que;
void bfs(int st) {
	memset(dis, -1, sizeof dis);
	dis[st] = 0; que.push_back(st); 
	while (que.size()) {
		int u = que.front(); que.pop_front();
		for (auto [v, w] : g[u]) {
			if (dis[v] == -1) {
				dis[v] = dis[u] + w;
				if (w == 0) que.push_front(v);
				else que.push_back(v);
			}
		}
	}
}

int main() {
	get(n, m); 
	rep(i,1,m) get(l, r), g[r].emplace_back(l - 1, 0), g[l - 1].emplace_back(r, 0);
	rep(i,1,n) g[i - 1].emplace_back(i, 1), g[i].emplace_back(i - 1, 1);
	bfs(0);
	rep(i,1,n) cout << (dis[i] < dis[i - 1]);
}



CF754E

给出一个字母阵 A,再给出一个部分字母没有确定的询问阵 B,求出左上角为位置 (i,j) 时能否匹配 (A[(i+x)mod n][(i+y)mod m]=B[i][j]),输出 01 矩阵表示答案。

1n,m,r,c400

昨天 WintersRain 发了个博客 vp 390 (Div. 2)
然后我看着他们没写这题 然后水一下

首先考虑朴素的 O(n4) 如何实现。令 Pn×m 的矩阵,Tr×c 的矩阵。

memset(ans, true, sizeof(ans));
for (int k = 1; k <= r; ++ k) for (int l = 1; l <= c; ++ l) if (T[k][l] != '?')
	for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) 
		ans[(i - k + n) % n][(j - l + m) % m] &= (T[k][l] == P[i][j]);

发现最终我们需要对答案数组的整行进行有条件的与操作。这使我们想到使用 bitset 优化进程。
P 矩阵中每行使用 26 个 bitset ,第 i 位保存 az 是否存在于这一位。我们发现朴素实现的第 4 层循环可以使用 bitset 表示。

因此采用 bitset 优化后可以做到 O(nmrcw),足以通过本题。

本题的思想类似于 CF914F,二者都是通过 bitset 进行带通配符的字符串匹配。

同时,本题还有采用多项式的解法,方式形如本题。时间复杂度为 O(nmclogn)。由于常数过大,在实际测评中远不如 bitset 做法优秀。

以及这题 r,c 可能远大于 n,m,注意直接取模。

code
#include <bits/stdc++.h>
using namespace std;
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 400 + 10;
int n, m, r, c;
char str[N];
bitset<N> ans[N], mask[26][N];

int main() {
	ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
	cin >> n >> m;
	rep(i,1,n) {
		ans[i].set();
		cin >> str + 1;
		rep(j,1,m) mask[str[j]-'a'][i].set(j);
	} cin >> r >> c;
	rep(i,1,r) {
		cin >> str + 1;
		rep(j,1,c) if (str[j] != '?') rep(k,1,n) 
			ans[(k - i % n + n) % n + 1] &= (mask[str[j] - 'a'][k] >> (j - 1 + m) % m) | (mask[str[j] - 'a'][k] << m - (j - 1 + m) % m);
	} 
	rep(i,1,n) {
		rep(j,1,m) cout << ans[i][j];
		cout << '\n';
	}
}
posted @   joke3579  阅读(174)  评论(6编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示